a tool for shared writing and social publishing
at feature/footnotes 199 lines 7.3 kB view raw
1"use client"; 2import { ShortcutKey } from "../../../components/Layout"; 3import { Media } from "../../../components/Media"; 4import { Popover } from "../../../components/Popover"; 5import { metaKey } from "src/utils/metaKey"; 6import { useEntitySetContext } from "../../../components/EntitySetProvider"; 7import { useState } from "react"; 8import { ActionButton } from "components/ActionBar/ActionButton"; 9import { HelpSmall } from "../../../components/Icons/HelpSmall"; 10import { isMac } from "src/utils/isDevice"; 11import { useIsMobile } from "src/hooks/isMobile"; 12 13export const HelpButton = (props: { noShortcuts?: boolean }) => { 14 let entity_set = useEntitySetContext(); 15 let isMobile = useIsMobile(); 16 17 return entity_set.permissions.write ? ( 18 <Popover 19 side={isMobile ? "top" : "right"} 20 align={isMobile ? "center" : "start"} 21 asChild 22 className="max-w-xs w-full p-0!" 23 trigger={<ActionButton icon={<HelpSmall />} label="About" />} 24 > 25 <div 26 className={`flex flex-col text-sm gap-2 p-3 text-secondary max-h-[70vh] overflow-y-auto p-2" : ""}`} 27 > 28 {/* about links */} 29 <HelpLink text="📖 Leaflet Manual" url="https://about.leaflet.pub" /> 30 <HelpLink text="💡 Make with Leaflet" url="https://make.leaflet.pub" /> 31 <HelpLink 32 text="✨ Explore Publications" 33 url="https://leaflet.pub/discover" 34 /> 35 <HelpLink text="📣 Newsletter" url="https://buttondown.com/leaflet" /> 36 {/* contact links */} 37 <div className="columns-2 gap-2"> 38 <HelpLink 39 text="🦋 Bluesky" 40 url="https://bsky.app/profile/leaflet.pub" 41 /> 42 <HelpLink text="💌 Email" url="mailto:contact@leaflet.pub" /> 43 </div> 44 {/* keyboard shortcuts: desktop only */} 45 <Media mobile={false}> 46 {!props.noShortcuts && ( 47 <> 48 <hr className="text-border my-1" /> 49 <div className="flex flex-col gap-1"> 50 <Label>Text Shortcuts</Label> 51 <KeyboardShortcut name="Bold" keys={[metaKey(), "B"]} /> 52 <KeyboardShortcut name="Italic" keys={[metaKey(), "I"]} /> 53 <KeyboardShortcut name="Underline" keys={[metaKey(), "U"]} /> 54 <KeyboardShortcut 55 name="Highlight" 56 keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "H"]} 57 /> 58 <KeyboardShortcut 59 name="Strikethrough" 60 keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "X"]} 61 /> 62 <KeyboardShortcut name="Inline Link" keys={[metaKey(), "K"]} /> 63 <KeyboardShortcut 64 name="Make Title" 65 keys={[metaKey(), isMac() ? "Opt" : "Alt", "1"]} 66 /> 67 <KeyboardShortcut 68 name="Make Heading" 69 keys={[metaKey(), isMac() ? "Opt" : "Alt", "2"]} 70 /> 71 <KeyboardShortcut 72 name="Make Subheading" 73 keys={[metaKey(), isMac() ? "Opt" : "Alt", "3"]} 74 /> 75 <KeyboardShortcut 76 name="Regular Text" 77 keys={[metaKey(), isMac() ? "Opt" : "Alt", "0"]} 78 /> 79 <KeyboardShortcut 80 name="Large Text" 81 keys={[metaKey(), isMac() ? "Opt" : "Alt", "+"]} 82 /> 83 <KeyboardShortcut 84 name="Small Text" 85 keys={[metaKey(), isMac() ? "Opt" : "Alt", "-"]} 86 /> 87 88 <Label>Block Shortcuts</Label> 89 {/* shift + up/down arrows (or click + drag): select multiple blocks */} 90 <KeyboardShortcut 91 name="Move Block Up" 92 keys={["Shift", metaKey(), "↑"]} 93 /> 94 <KeyboardShortcut 95 name="Move Block Down" 96 keys={["Shift", metaKey(), "↓"]} 97 /> 98 {/* cmd/ctrl-a: first selects all text in a block; again selects all blocks on page */} 99 {/* cmd/ctrl + up/down arrows: go to beginning / end of doc */} 100 101 <Label>Canvas Shortcuts</Label> 102 <OtherShortcut name="Add Block" description="Double click" /> 103 <OtherShortcut name="Select Block" description="Long press" /> 104 105 <Label>Outliner Shortcuts</Label> 106 <KeyboardShortcut 107 name="Make List" 108 keys={[metaKey(), isMac() ? "Opt" : "Alt", "L"]} 109 /> 110 {/* tab / shift + tab: indent / outdent */} 111 <KeyboardShortcut 112 name="Toggle Checkbox" 113 keys={[metaKey(), "Enter"]} 114 /> 115 <KeyboardShortcut 116 name="Toggle Fold" 117 keys={[metaKey(), "Shift", "Enter"]} 118 /> 119 <KeyboardShortcut 120 name="Fold All" 121 keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↑"]} 122 /> 123 <KeyboardShortcut 124 name="Unfold All" 125 keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↓"]} 126 /> 127 </div> 128 </> 129 )} 130 </Media> 131 {/* links: terms and privacy */} 132 <hr className="text-border my-1" /> 133 {/* <HelpLink 134 text="Terms and Privacy Policy" 135 url="https://leaflet.pub/legal" 136 /> */} 137 <div> 138 <a href="https://leaflet.pub/legal" target="_blank"> 139 Terms and Privacy Policy 140 </a> 141 </div> 142 </div> 143 </Popover> 144 ) : null; 145}; 146 147const KeyboardShortcut = (props: { name: string; keys: string[] }) => { 148 return ( 149 <div className="flex gap-2 justify-between items-center"> 150 {props.name} 151 <div className="flex gap-1 items-center font-bold"> 152 {props.keys.map((key, index) => { 153 return <ShortcutKey key={index}>{key}</ShortcutKey>; 154 })} 155 </div> 156 </div> 157 ); 158}; 159 160const OtherShortcut = (props: { name: string; description: string }) => { 161 return ( 162 <div className="flex justify-between items-center"> 163 <span>{props.name}</span> 164 <span> 165 <strong>{props.description}</strong> 166 </span> 167 </div> 168 ); 169}; 170 171const Label = (props: { children: React.ReactNode }) => { 172 return <div className="text-tertiary font-bold pt-2 ">{props.children}</div>; 173}; 174 175const HelpLink = (props: { url: string; text: string }) => { 176 const [isHovered, setIsHovered] = useState(false); 177 const handleMouseEnter = () => { 178 setIsHovered(true); 179 }; 180 const handleMouseLeave = () => { 181 setIsHovered(false); 182 }; 183 return ( 184 <a 185 href={props.url} 186 target="_blank" 187 className="py-2 px-2 rounded-md flex flex-col gap-1 bg-border-light hover:bg-border hover:no-underline" 188 style={{ 189 backgroundColor: isHovered 190 ? "rgb(var(--accent-light))" 191 : "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 75%)", 192 }} 193 onMouseEnter={handleMouseEnter} 194 onMouseLeave={handleMouseLeave} 195 > 196 <strong>{props.text}</strong> 197 </a> 198 ); 199};